import { ChangeDetectionStrategy, Component, Input, QueryList } from '@angular/core';
import { uniqueId as _uniqueId } from 'lodash';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { CommonModule } from '@angular/common';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TransferModule } from './transfer.module';
import { FilterType, ModeType, TransferColumnItem, TransferModeTypeEnum } from './transfer.interface';

@Component({
  template: `
    <lv-transfer
      class="customer-transfer"
      [lvSourceColumns]="source.cols"
      [lvSourceData]="source.data"
      [lvSourceSelection]="source.selection"
      [lvTargetColumns]="target.cols"
      (lvStateChange)="stateChange($event)"
      (lvChange)="change($event)"
      [lvMode]="lvMode"
    ></lv-transfer>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TestTransferComponent {
  @Input() lvMode: ModeType = TransferModeTypeEnum.DEFAULT;
  source: { cols: TransferColumnItem[]; data: any[]; selection: any[] } = {
    cols: [],
    data: [],
    selection: [],
  };

  target: { cols: TransferColumnItem[] } = { cols: [] };

  ngOnInit() {
    this.initColumns();
    this.initData();
  }

  initColumns() {
    const sourceColumns = [
      {
        key: 'id',
        label: 'ID',
        disabled: true, // 是否可以隐藏/显示
        isHidden: true,
        hidden: true, // 列显示隐藏操作项是否显示
      },
      {
        key: 'name',
        label: 'Name',
        disabled: true,
        isHidden: false,
        filterType: FilterType.SEARCH,
      },
      {
        key: 'age',
        label: 'Age',
        disabled: false,
        isHidden: false,
      },
    ];

    this.source.cols = sourceColumns;
    this.target.cols = sourceColumns.map((item) => ({ ...item }));
  }

  initData() {
    this.source.data = Array.from({ length: 50 }).map((item, key) => this.initUser(key, key % 4 === 0));

    this.source.selection = [this.source.data[1]];
  }

  initUser(key: number, disabled: boolean) {
    const colors = ['red', 'blue', 'green', 'orange'];
    const temp = {
      id: _uniqueId('uid'),
      key: _uniqueId('uid'),
      name: `user${key}`,
      age: 10 + key,
      expand: false,
      random: parseInt(`${Math.random() * 100}`, 10),
      disabled,
    };
    temp['color'] = colors[temp.random % colors.length];
    return temp;
  }
}

describe('TransferComponent', () => {
  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [TestTransferComponent],
      imports: [TransferModule, CommonModule, NoopAnimationsModule],
      teardown: { destroyAfterEach: true },
    }).compileComponents();
  }));
  describe('render', () => {
    let component: TestTransferComponent;
    let fixture: ComponentFixture<TestTransferComponent>;
    const renderComp = fakeAsync((para) => {
      for (const [key, value] of Object.entries(para)) {
        component[key] = value;
      }
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
      tick(1500);
    });

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestTransferComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      tick(500);
    }));

    it('basic', () => {
      renderComp({});
    });

    it('simple', () => {
      renderComp({ lvMode: TransferModeTypeEnum.SIMPLE });
    });
  });
  describe('function', () => {
    let fixture: ComponentFixture<TestTransferComponent>;
    const detectChange = () => {
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
      fixture.detectChanges();
      tick(500);
    };

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TestTransferComponent);
      detectChange();
    }));

    it('select', fakeAsync(() => {
      const getRow = (row: number) =>
        fixture.nativeElement.querySelector(
          `lv-transfer lv-transfer-panel:nth-child(1) tbody tr:nth-child(${row})`,
        ) as HTMLElement;
      const getSelectedTr = () =>
        fixture.nativeElement.querySelectorAll(
          'lv-transfer lv-transfer-panel:nth-child(3) tbody tr',
        ) as QueryList<HTMLElement>;
      const getSelectedNum = () => getSelectedTr().length as number;
      expect(getSelectedNum()).toBe(1);
      const user2 = getRow(3);
      user2.dispatchEvent(new Event('click'));
      detectChange();
      expect(getSelectedNum()).toBe(2);
      getSelectedTr()[1].querySelector('.lv-transfer-item-remove').dispatchEvent(new Event('click'));
      detectChange();
      expect(getSelectedNum()).toBe(1);
    }));

    const target = {
      source: 1,
      target: 3,
    };
    const paginator = {
      next: '.lv-paginator-next',
      previous: '.lv-paginator-previous',
    };
    const getPaginatorButton = (nth: number, paginatorClass: string) =>
      fixture.nativeElement.querySelector(
        `lv-transfer lv-transfer-panel:nth-child(${nth}) ${paginatorClass}`,
      ) as HTMLElement;
    const getSourceNextButton = () => getPaginatorButton(target.source, paginator.next);
    const getSourcePreviousButton = () => getPaginatorButton(target.source, paginator.previous);
    const getTargetNextButton = () => getPaginatorButton(target.target, paginator.next);
    const getTargetPreviousButton = () => getPaginatorButton(target.target, paginator.previous);
    const getSelectedTr = (nth: number) =>
      fixture.nativeElement.querySelectorAll(
        `lv-transfer lv-transfer-panel:nth-child(${nth}) tbody tr`,
      ) as QueryList<HTMLElement>;
    const getSelected = (getNextButton: () => HTMLElement, getSelectedLen: () => number) => {
      let count = 0;
      while (!getNextButton().classList.contains('disabled')) {
        count += getSelectedLen();
        getNextButton().dispatchEvent(new Event('click'));
        detectChange();
      }
      count += getSelectedLen();
      return count;
    };

    it('selectAll', fakeAsync(() => {
      const getAllButton = (nth: number) =>
        fixture.nativeElement.querySelector(
          `lv-transfer lv-transfer-panel:nth-child(${nth}) .lv-transfer-panel-header-operate button`,
        ) as HTMLElement;
      const getSelectAllButton = () => getAllButton(target.source);
      const getRemoveAllButton = () => getAllButton(target.target);

      const getCheckedLen = () => fixture.nativeElement.querySelectorAll('.lv-icon-checked').length as number;
      getSelectAllButton().dispatchEvent(new Event('click'));
      detectChange();

      const getSourceSelected = () => getSelected(getSourceNextButton, getCheckedLen);
      const getTargetSelected = () => getSelected(getTargetNextButton, () => getSelectedTr(target.target).length);
      expect(getSourceSelected()).toBe(getTargetSelected());
      getRemoveAllButton().dispatchEvent(new Event('click'));
      detectChange();
      const getSourceSelectedAfterRemove = () => getSelected(getSourcePreviousButton, getCheckedLen);
      const getTargetSelectedAfterRemove = () =>
        getSelected(getTargetPreviousButton, () => getSelectedTr(target.target).length);
      expect(getSourceSelectedAfterRemove()).toBe(getTargetSelectedAfterRemove());
    }));

    it('search', fakeAsync(() => {
      const getSearchButton = (nth: number) =>
        fixture.nativeElement.querySelector(
          `lv-transfer lv-transfer-panel:nth-child(${nth}) .lv-cell-custom-operation .lv-transfer-table-customer-filter i`,
        ) as HTMLElement;
      const getSourceSearchButton = () => getSearchButton(target.source);
      const getTargetSearchButton = () => getSearchButton(target.target);
      const search = (getSearchButton: () => HTMLElement, value: string) => {
        getSearchButton().dispatchEvent(new Event('click'));
        detectChange();
        const getInput = () => document.querySelector('lv-search input') as HTMLInputElement;
        getInput().value = value;
        getInput().dispatchEvent(new Event('input'));
        const event = new KeyboardEvent('keydown', {
          key: 'Enter',
          code: 'Enter',
          which: 13,
          keyCode: 13,
          charCode: 13,
        });
        getInput().focus();
        getInput().dispatchEvent(event);
        getSearchButton().dispatchEvent(new Event('click'));
        detectChange();
      };
      search(getSourceSearchButton, '0');
      expect(getSelected(getSourceNextButton, () => getSelectedTr(target.source).length)).toBe(5);
      search(getTargetSearchButton, '1');
      expect(getSelected(getTargetNextButton, () => getSelectedTr(target.target).length)).toBe(1);
      const clear = (getSearchButton: () => HTMLElement) => {
        getSearchButton().dispatchEvent(new Event('click'));
        detectChange();
        const getInputRemove = () => document.querySelector('lv-search button') as HTMLElement;
        getInputRemove().dispatchEvent(new Event('click'));
        getSearchButton().dispatchEvent(new Event('click'));
        detectChange();
      };
      clear(getSourceSearchButton);
      expect(getSelected(getSourceNextButton, () => getSelectedTr(target.source).length)).toBe(50);
      clear(getTargetSearchButton);
      expect(getSelected(getTargetNextButton, () => getSelectedTr(target.target).length)).toBe(1);
    }));
  });
});
